from utils.copy import coop_deepcopy

from cranesched.instance.warehouse.warehouse import Component
from cranesched.instance.warehouse.crane.container import Container
from cranesched.instance.warehouse.crane.movable2d import Movable2D


class Crane(Container, Movable2D, Component):
    def __init__(self, id, capacity=1, items=(), time=0.0, 
                 x=0.0, xspeed=1.0, xsize=1.0, 
                 y=0.0, yspeed=1.0, ysize=1.0):
        Container.__init__(self, capacity, items)
        Movable2D.__init__(self, id, time, x, xspeed, xsize, y, yspeed, ysize)
        Component.__init__(self, id)
        self.assignments = []
        
    def __deepcopy__(self, memo):
        clone = coop_deepcopy(self, memo, Crane.__bases__)
        clone.assignments = []
        return clone
        
    def __repr__(self):
        return "<Crane-%s @ %s (t=%s) %s>" % (self.id, self.position, self.time, self.items)
        
    def _set_warehouse(self, warehouse):
        if self.warehouse is not None:
            for item in self.items:
                self.warehouse.set_location(item, None)
        Component._set_warehouse(self, warehouse)
        if self.warehouse is not None:
            for item in self.items:
                self.warehouse.set_location(item, self.id)
                
    def append(self, item):
        Container.append(self, item)
        self.warehouse.set_location(item, self.id)
        
    def pop(self):
        item = Container.pop(self)
        self.warehouse.set_location(item, None)
        
    def _can_fulfill(self, assignment):
        """In addition to Container._can_execute() and Movable2D._can_fulfill(), this method checks 
        if the activity's target y-coordinate is reachable (this checks against warehouse limits, 
        which are unknown to Movable2D, hence it is checked here). Please refer to the methods 
        mentioned above for more information."""
        activity = assignment.activity
        if activity.y not in self.warehouse.ylimits:
            return Movable2D.UNREACHABLE_POSITION
        error = Container._can_execute(self, activity.action)
        if error is not None:
            return error
        return Movable2D._can_fulfill(self, assignment)
        
    def fulfill(self, assignment):
        Movable2D.fulfill(self, assignment)
        Container.execute(self, assignment.activity.action)
        self.assignments.append(assignment)
        
    def undo(self):
        assignment = self.assignments.pop()
        Movable2D.undo(self)
        Container.execute(self, assignment.activity.action.reversed())
        return assignment
        
